#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/time.h>

#ifdef __aarch64__
#include "uadk/v1/wd.h"
#include "uadk/v1/wd_bmm.h"
#include "uadk/v1/wd_aead.h"
#endif

#define SHOW_LOG false
#define TEST_TIMES    1
#define HASH_LENGTH 1024 * 1024 * 10
#define SQE_SIZE 128
#define MAX_BLOCK_SZ 16 * 1024 * 1024
#define MAX_BLOCK_NM    128

uint8_t *data = NULL;

void ifm_str2uint(uint32_t length, uint8_t *dst, const char *src)
{
    uint32_t len = length / 2;
    uint32_t i;
    for (i = 0; i < length; ++i)
    {
        dst[i / 2] = 16 * ( src[i] < 'a' ? (src[i] - '0') : ((src[i] - 'a') + 10) );
        ++i;
        dst[i / 2] += ( src[i] < 'a' ? (src[i] - '0') : ((src[i] - 'a') + 10) );
    }
}

#ifdef __aarch64__
static void sec_dump_bd(unsigned char *bd, unsigned int len)
{
    unsigned int i;
    char hex_digest[len * 2 + 1];

    for (i = 0; i < len; i++)
    {
        sprintf(hex_digest + i * 2, "%02x", bd[i]);
    }
    WD_ERR("authtag: %s", hex_digest);
    WD_ERR("\n");
}

// 测试UADK中v1接口的hash算法（MD5），参考UADK中的test_hisi_sec.c
void uadk_v1_md5_damo(struct wd_queue *in_q, void *in_pool, unsigned int times)
{
    struct timeval start_tval;
    struct timeval cur_tval;
    gettimeofday(&start_tval, NULL);
    int time_used;
    const uint8_t z16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
    const char iv[] =  "cafebabefacedbaddecaf888";
    const char key[] = "feffe9928665731c6d6a8f9467308308";
    const char auth[] = "feedfacedeadbeeffeedfacedeadbeefabaddad2";
    const char clear[] = "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39";
    uint8_t dst[60];
    uint8_t digest[16];
    int ret;
    uint8_t *data_pt;
    size_t length = strlen(data);
    size_t total_len = 0;

    if (SHOW_LOG)
        printf("==============This is test for uadk v1 MD5: %s\n", data);

    struct wd_queue q;
    struct wd_queue *pq;
    struct wd_blkpool_setup pool_setup;
    void *pool = NULL;
    if (in_q == NULL)
    {
        memset(&q, 0, sizeof(q));
        q.capa.alg = "aead";
        wd_request_queue(&q);
        pq = &q;
    }
    else
    {
        pq = in_q;
    }
    if (in_pool == NULL)
    {
        memset(&pool_setup, 0, sizeof(pool_setup));
        pool_setup.block_size = MAX_BLOCK_SZ; //set pool  inv + key + in + out
        pool_setup.block_num = MAX_BLOCK_NM;
        pool_setup.align_size = SQE_SIZE;
        pool = wd_blkpool_create(pq, &pool_setup);
    }
    else
    {
        pool = in_pool;
    }
    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============1 %d times: %d usec\n", TEST_TIMES, time_used);

    gettimeofday(&start_tval, NULL);
    struct wcrypto_aead_ctx_setup setup;
    struct wcrypto_aead_op_data opdata;
    void *ctx = NULL;

    memset(&setup, 0, sizeof(setup));
    memset(&opdata, 0, sizeof(opdata));
    
    setup.calg = WCRYPTO_CIPHER_AES;
    setup.cmode = WCRYPTO_CIPHER_GCM;
    setup.br.alloc = (void *)wd_alloc_blk;
    setup.br.free = (void *)wd_free_blk;
    setup.br.iova_map = (void *)wd_blk_iova_map;
    setup.br.iova_unmap = (void *)wd_blk_iova_unmap;
    setup.br.get_bufsize = (void *)wd_blksize;
    setup.br.usr = pool;
    ctx = wcrypto_create_aead_ctx(pq, &setup);
    if (!ctx) {
        printf("no ctx\n");
        return;
    }

    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============2 %d times: %d usec\n", TEST_TIMES, time_used);
    
    gettimeofday(&start_tval, NULL);

    uint8_t key_uint[16];
    ifm_str2uint(strlen(key), key_uint, key);

    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============3 %d times: %d usec\n", TEST_TIMES, time_used);
    
    gettimeofday(&start_tval, NULL);
    
    ret = wcrypto_set_aead_ckey(ctx, key_uint, 16);
		if (ret) {
			printf("no ckey\n");
            return;
		}

    ret = wcrypto_aead_setauthsize(ctx, 16);
	if (ret) {
		printf("no authsize fail\n");
            return;
	}
    uint8_t z12[40];
    ifm_str2uint(strlen(iv), z12, iv);
    opdata.iv = wd_alloc_blk(pool);
    memcpy(opdata.iv, z12, strlen(iv)/2);
    opdata.iv_bytes = strlen(iv)/2;
    // 初始化opdata
    int i = 0;

    opdata.in = wd_alloc_blk(pool);
    opdata.out = wd_alloc_blk(pool);
    opdata.out_bytes = strlen(auth)/2 + HASH_LENGTH + 16;
    opdata.out_buf_bytes = MAX_BLOCK_SZ;
    memset(opdata.out, 0, opdata.out_bytes);

    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============4 %d times: %d usec\n", TEST_TIMES, time_used);
    
    gettimeofday(&start_tval, NULL);
    
    uint8_t auth_uint[20];
    ifm_str2uint(strlen(auth), auth_uint, auth);

    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============5 %d times: %d usec\n", TEST_TIMES, time_used);
    
    gettimeofday(&start_tval, NULL);
    
    
    memcpy(opdata.in, auth_uint, strlen(auth)/2);
    opdata.assoc_size = strlen(auth)/2;

    uint8_t src[60];
    ifm_str2uint(strlen(clear), src, clear);

    memcpy(opdata.in + opdata.assoc_size, data, HASH_LENGTH);

    opdata.op_type = WCRYPTO_CIPHER_ENCRYPTION_DIGEST;
    opdata.in_bytes = 0;
    opdata.in_bytes = HASH_LENGTH;
    opdata.priv = NULL;
    // printf("start aead\n");
    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============6 %d times: %d usec\n", TEST_TIMES, time_used);
    
    gettimeofday(&start_tval, NULL);


    wcrypto_do_aead(ctx, &opdata, NULL);

    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("wcrypto_do_aead %d times: %d usec\n", TEST_TIMES, time_used);
    
gettimeofday(&start_tval, NULL);
    if (opdata.iv)
        wd_free_blk(pool, opdata.iv);
    if (opdata.in)
        wd_free_blk(pool, opdata.in);
    if (opdata.out)
        wd_free_blk(pool, opdata.out);
    if (ctx)
        wcrypto_del_aead_ctx(ctx);
    if (in_q == NULL)
    {
        wd_release_queue(&q);
    }
    if (in_pool == NULL)
    {
        if (pool)
            wd_blkpool_destroy(pool);
    }
gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============7 %d times: %d usec\n", TEST_TIMES, time_used);
    return;
}
#endif

int main(int argc, char **argv)
{
    struct timeval start_tval;
    struct timeval cur_tval;
    int time_used;
    
    data=malloc(HASH_LENGTH * sizeof(uint8_t));
    memset(data, 0, sizeof(data));
    for (int i = 0; i < HASH_LENGTH; i++)
    {
        data[i] = rand() % 26 + 'a';
    }

#ifdef __aarch64__
    // 测试UADK V1接口MD5的性能，每次都重新初始化
    gettimeofday(&start_tval, NULL);

    for (int i = 0; i < TEST_TIMES; i++)
    {
        uadk_v1_md5_damo(NULL, NULL, TEST_TIMES);
    }
    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============uadk v1 MD5 %d times: %d usec\n", TEST_TIMES, time_used);
    
    // 测试UADK V1接口MD5的性能，统一初始化一次
    struct wd_queue q;
    memset(&q, 0, sizeof(q));
    q.capa.alg = "aead";
    wd_request_queue(&q);
    void *pool = NULL;
    struct wd_blkpool_setup pool_setup;
    memset(&pool_setup, 0, sizeof(pool_setup));
    pool_setup.block_size = MAX_BLOCK_SZ; //set pool  inv + key + in + out
    pool_setup.block_num = MAX_BLOCK_NM;
    pool_setup.align_size = SQE_SIZE;
    pool = wd_blkpool_create(&q, &pool_setup);
    gettimeofday(&start_tval, NULL);
    for (int i = 0; i < TEST_TIMES; i++)
    {
        uadk_v1_md5_damo(&q, pool, TEST_TIMES);
    }
    
    gettimeofday(&cur_tval, NULL);
    time_used = (cur_tval.tv_sec - start_tval.tv_sec) * 1000000 +
            cur_tval.tv_usec - start_tval.tv_usec;
    printf("==============uadk v1 MD5 by once init, run %d times: %d usec\n", TEST_TIMES, time_used);

    if (pool)
        wd_blkpool_destroy(pool);
    wd_release_queue(&q);
#endif
    
    if (data)
    {
        free(data);
    }

    return 0;
}
